package core;

import utilities.MagatzemDades;
import java.beans.*;
import java.util.*;
import java.lang.reflect.*;

/**
 * Created with IntelliJ IDEA.
 * User: Jordi
 * Date: 28/12/12
 * Time: 9:24
 * To change this template use File | Settings | File Templates.
 */
public abstract class ObjectePersistentAbst implements ObjectePersistent, java.io.Serializable, Cloneable {

    protected boolean modificat = false;
    protected Map valorsEnsenyats;
    protected static Object[] ARG_LECT_BUIT= new Object[0];
    protected long persistentFactoryId = -1;
    protected String charset = "UTF-8";
    protected MagatzemDades valorsOriginals = null;
    protected int objecteId = 0;
    protected char estatPersistencia = INDEFINIT;
    protected boolean carregat = false;
    protected boolean carregant = false;
    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        Object valor = null;
        Method[] metodes = getClass().getMethods();
        for (int i = 0;i < metodes.length;i++){
            String nomMetode = metodes[i].getName();
            if (nomMetode.equals("getValoresComoHTML") || nomMetode.equals("getClass")){
                continue;
            }
            if ((nomMetode.startsWith("get") || nomMetode.startsWith("is")) && metodes[i].getParameterTypes().length == 0){
                try{
                    valor = llegirValor(metodes[i]);
                }catch (Throwable t){
                    continue;
                }
                String fieldCaracterInicial = "";
                if (nomMetode.startsWith("is")){
                    fieldCaracterInicial = nomMetode.substring(2, 3).toLowerCase();
                    sb.append(fieldCaracterInicial);
                    sb.append(nomMetode.substring(3));
                }else if (nomMetode.startsWith("get")){
                    fieldCaracterInicial = nomMetode.substring(3, 4).toLowerCase();
                    sb.append(fieldCaracterInicial);
                    sb.append(nomMetode.substring(4));
                }
                sb.append("=");
                sb.append((valor == null)?"":valor);
                sb.append("&");
            }
        }
        sb.append("valorsEnsenyats=" + (valorsEnsenyats == null?"":valorsEnsenyats.toString()));
        return sb.toString();
    }
    @Override
    public void setValorsOriginals(MagatzemDades valorsOriginals) {
        this.valorsOriginals = valorsOriginals;
    }
    @Override
    public MagatzemDades getValorsOriginals() {
        return this.valorsOriginals;
    }
    @Override
    public void setValorOriginal(String nomCamp, Object valor) {
        if (valorsOriginals == null) {
            throw new IllegalArgumentException("setvalorsOriginals: MagatzemDades no esta preparat");
        }
        valorsOriginals.setObjecte(nomCamp, valor);
    }
    @Override
    public Object getValorOriginal(String nomCamp) {
        Object value = (valorsOriginals == null)? null:valorsOriginals.getObjecte(nomCamp);
        return value;
    }
    @Override
    public char getEstatPersistencia(){
        return estatPersistencia;
    }
    @Override
    public void setEstatPersistencia(char estatPersistencia){
        this.estatPersistencia = estatPersistencia;
    }
    /** Leer la propiedad de objetoId.
     * @return Value of property objetoId.
     *
     */
    @Override
    public int getObjecteId() {
        return objecteId;
    }
    /**
     * Cada vez que FactoriaObjetoPersistente se carga este objeto se le asigna
     * un objetoId que es válido solamente para el mismo
     * FactoriaObjetoPersistente (vea persistenteFactoryId).
     * Si el identificador objetoId de ObjetoPersistente se usa con otro
     * FactoriaObjetoPersistente es preciso conseguir un nuevo identificador
     * objetoId cuando se use con DomainObjectFactory.
     * [setObjeto(...), cargar(), crear(), almacenar(), eliminar()
     */
    @Override
    public void setObjecteId(int objecteId) {
        this.objecteId = objecteId;
    }
    /**
     * Solo sirve para marcar la modificación de datos, la modificación en curso se encuentra en
     * FactoriaObjetoPersistente --> GrabarDatos
     * Normalmente este método es llamado desde FactoriaObjetoPersistente.[crear(ObjetoPersistente), almacenar(ObjetoPersistente),
     eliminar(ObjetoPersistente)]
     * @return Boolean para marcar la modificación en curso
     */
    @Override
    public boolean guardar() {
        if (valorsOriginals != null && valorsOriginals.getComptadorCamps() > 0){
            setEstatPersistencia(GUARDAT);
            return true;
        }
        return false;
    }
    @Override
    public boolean esborrar() {
        if (valorsOriginals != null && valorsOriginals.getComptadorCamps() > 0){
            setEstatPersistencia(ESBORRAT);
            return true;
        }
        return false;
    }
    @Override
    public boolean crear() {
        if (valorsOriginals == null || valorsOriginals.getComptadorCamps() < 1){
            setEstatPersistencia(CREAT);
            return true;
        }
        return false;
    }
    /**
     * rellenar valorsOriginals con valores desde ObjetoPersistente.
     */
    @Override
    public boolean syncOriginal(){
        boolean sync = false;
        if (valorsOriginals != null && valorsOriginals.getComptadorCamps() > 0){
            try{
                setEstatPersistencia(ORIGINAL);
                for(int fieldIndex = 1; fieldIndex <= valorsOriginals.getComptadorCamps(); fieldIndex++){
                    String nomCamp = valorsOriginals.getNomCamp(fieldIndex);
                    PropertyDescriptor pd = new PropertyDescriptor(nomCamp, this.getClass());
                    Object obj = llegirValor(pd.getReadMethod());
                    valorsOriginals.setObjecte(fieldIndex, obj);
                }
                sync = true;
                setModificat(false);
            }catch (Throwable t){
                throw new RuntimeException(t);
            }
        }
        return sync;
    }
    /**
     * rellenar ObjetoPersistente con valores desde valorsOriginals.
     */
    @Override
    public boolean carregar() {
        setCarregant(true);
        if (valorsOriginals != null && valorsOriginals.getComptadorCamps() > 0){
            try{
                List listParam = new ArrayList(1);
                for(int fieldIndex = 1; fieldIndex <= valorsOriginals.getComptadorCamps(); fieldIndex++){
                    listParam.clear();
                    String nomCamp = valorsOriginals.getNomCamp(fieldIndex);
                    Object obj = valorsOriginals.getObjecte(fieldIndex);
                    PropertyDescriptor pd = new PropertyDescriptor(nomCamp, this.getClass());
                    listParam.add(obj);
                    escriuValor(pd.getWriteMethod(), listParam.toArray());
                }
            }catch(Throwable t){
                throw new RuntimeException(t);
            }
            setCarregant(false);
            setCarregat(true);
            setModificat(false);
            return true;
        }
        setCarregant(false);
        return false;
    }
    /** Leer la propiedad de cargado.
     * @return Value of property carregat.
     *
     */
    @Override
    public boolean isCarregat() {
        return carregat;
    }
    /** Asignar nueva propiedad a cargado.
     * @param carregat New valor of property cargado.
     *
     */
    @Override
    public void setCarregat(boolean carregat) {
        this.setEstatPersistencia(ORIGINAL);
        this.carregat = carregat;
    }
    /**
     * Éste es el método más conveniente para usar con JSP.
     * Debe asegurarse de no llamar a este método desde otro método
     * de este mismo objeto (o subclase) que empiece con 'get',
     * o se conseguirá entrar en un bucle interminable.
     */
    public String getValorsComHTML() {
        StringBuffer sb = new StringBuffer();
        Object obj = null;
        Method[] methods = getClass().getMethods();
        for (int i = 0;i < methods.length;i++){
            String nomMethod = methods[i].getName();
            if (nomMethod.equals("getValorsComHTML") || nomMethod.equals("getClass")){
                continue;
            }
            if ((nomMethod.startsWith("get") || nomMethod.startsWith("is")) && methods[i].getParameterTypes().length == 0){
                try{
                    obj = llegirValor(methods[i]);
                }catch (Throwable t){
                    continue;
                }
                String primerCharCamp = "";
                if (nomMethod.startsWith("is")){
                    primerCharCamp = nomMethod.substring(2, 3).toLowerCase();
                    sb.append(primerCharCamp);
                    sb.append(nomMethod.substring(3));
                }else if (nomMethod.startsWith("get")){
                    primerCharCamp = nomMethod.substring(3, 4).toLowerCase();
                    sb.append(primerCharCamp);
                    sb.append(nomMethod.substring(4));
                }
                sb.append("=");
                if (obj == null){
                    sb.append("");
                }else{
                    try{
                        sb.append(java.net.URLEncoder.encode(obj.toString(), charset));
                    }catch (java.io.UnsupportedEncodingException uee){
                        throw new RuntimeException(uee);
                    }
                }
                sb.append("&");
            }
        }
        sb.deleteCharAt(sb.length() - 1);
        return sb.toString();
    }
    /**
     * Indicador de que este objeto se encuentra en proceso de carga
     * de los valores desde la BBDD
     */
    @Override
    public boolean isCarregant() {
        return this.carregant;
    }
    @Override
    public void setCarregant(boolean carregant) {
        this.carregant = carregant;
    }
    /**
     * Indicador de que este objeto ha sido modificado.
     * No se llama automaticamente, debe ser invocado por el programador
     *
     */
    @Override
    public boolean isModificat() {
        return this.modificat;
    }
    @Override
    public void setModificat(boolean modificat) {
        this.modificat = modificat;
    }
    /**
     * Muestra el valor para cada campo en particular. Por ejemplo:
     * el valor del campo es "123" pero la visualización será "XYZ"
     */
    @Override
    public void setEnsenyarValor(String camp, Object valorEnsenyar){
        if (valorsEnsenyats == null) valorsEnsenyats = new HashMap();
        valorsEnsenyats.put(camp, valorEnsenyar);
    }
    @Override
    public Object getEnsenyarValor(String nomCamp){
        Object ensenyar;
        try{
            if (valorsEnsenyats == null){
                PropertyDescriptor pDesc = new PropertyDescriptor(nomCamp, this.getClass());
                ensenyar = llegirValor(pDesc.getReadMethod());
            }else{
                ensenyar = valorsEnsenyats.get(nomCamp);
            }
        }catch (Throwable t){
            throw new RuntimeException(t);
        }
        return ensenyar;
    }
    @Override
    public void setPersistentFactoryId(long persistentFactoryId){
        this.persistentFactoryId = persistentFactoryId;
    }
    @Override
    public long getPersistentFactoryId(){
        return this.persistentFactoryId;
    }
    protected void escriuValor(Method metode, Object[] valor) throws Throwable{
        try{
            metode.invoke(this, valor);
        }catch (Throwable t){
            if (t instanceof InvocationTargetException){
                throw ((InvocationTargetException) t).getTargetException();
            }else{
                throw t;
            }
        }
    }
    protected Object llegirValor(Method metode) throws Throwable{
        Object valor = null;
        try{
            valor = metode.invoke(this, ARG_LECT_BUIT);
        }catch (Throwable t){
            if (t instanceof InvocationTargetException){
                throw ((InvocationTargetException) t).getTargetException();
            }else{
                throw t;
            }
        }
        return valor;
    }
}
